import os

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.optimize import linprog

from topsis_zbj import readData_zbj
from topsis_zbj.readData_zbj import get_Pinfo_Data_Search
from topsis_zbj.topsis_config import topsis_config

plt.rcParams['font.sans-serif'] = ['SimHei']  # 步骤一（替换sans-serif字体）
plt.rcParams['axes.unicode_minus'] = False  # 步骤二（解决坐标轴负数的负号显示问题）
WEIGHTFLOW_DOWN = topsis_config.WEIGHTFLOW_DOWN
WEIGHTFLOW_UP = topsis_config.WEIGHTFLOW_UP
SUM_WEIGHT = topsis_config.SUM_WEIGHT

# 显示pandas dataframe的所有行
pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)


# 熵权法
def entropyWeight(data):
    data = np.array(data)
    # 归一化
    P = data / data.sum(axis=0)
    # 计算熵值
    E = np.nansum(-P * np.log(P) / np.log(len(data)), axis=0)
    # 计算权系数
    return (1 - E) / (1 - E).sum()


# 读取数据，并将id和产出数据分离
def topsis_read_data(dir, sheetname):
    dataset = pd.read_excel(dir, sheet_name=sheetname)
    # print(dataset)
    column_list = {}
    try:
        column_list = dataset[topsis_config.bid_fields]
    except Exception as e:
        print("列名出错，读取失败！")
        print(e)
        exit()
    dataset = column_list.fillna(0)
    dataset = dataset.mask(dataset == True, 1)
    dataset = dataset.mask(dataset == False, 0)
    uid = dataset.iloc[:, np.r_[0]]  # 获取第一列
    outcomes = dataset[topsis_config.FIELDS_OUTCOME]  # 获取后面的列
    return uid, outcomes


# 用每个分数的排名来代替原始分数
def calculate_rank(data):
    data_columns = []
    for column in data.columns:
        co = data[column]
        co1 = {"uid": co.index, column: co.values}
        co1 = pd.DataFrame(co1)
        data_columns.append(co1)
    rank_data = None
    index = 0
    for i in data_columns:
        i = i.sort_values(by=i.columns[1], ascending=False)
        sorted_columns_list = i[i.columns[1]].T.values.tolist()
        sorted_set = set(sorted_columns_list)
        rank_dict = {}
        for v in sorted_set:
            rank_dict[v] = sorted_columns_list.index(v)
        for j in range(len(i)):
            i.iloc[j, 1] = rank_dict[i.iloc[j, 1]] + 1
        if index == 0:
            rank_data = i
        else:
            rank_data = pd.merge(rank_data, i, how='right', on="uid")
        index = 1
    return rank_data


# 线性规划计算权重
def get_weights(data, weights_list):
    res = []
    res_quan = []
    for row in range(data.shape[0]):
        c = -1 * data.iloc[row]
        A_eq = np.array([[1] * data.shape[1]])
        B_eq = np.array([SUM_WEIGHT])
        bo = []
        for co in range(len(data.columns)):
            up_bo = weights_list[data.columns[co]] + WEIGHTFLOW_UP
            low_bo = weights_list[data.columns[co]] - WEIGHTFLOW_DOWN
            if low_bo < 0:
                low_bo = 0
            bo.append([low_bo, up_bo])
        bo = tuple(bo)
        linear_res = linprog(c, A_eq=A_eq, b_eq=B_eq, bounds=bo)
        res.append(linear_res['x'].tolist())
    return np.array(res)


# topsis TODO 尝试不用排名
def topsis(uid, data, weight=None):
    data = data.fillna(0)
    # 使用排名代替
    new_data = (data - data.min()) / (data.max() - data.min())
    new_data = new_data.fillna(0)
    data = new_data
    new_data = pd.concat([uid, new_data], axis=1)
    # 最优最劣方案
    Z = pd.DataFrame([data.min(), data.max()], index=['负理想解', '正理想解'])
    # 求权重使得离正理想解的距离最小。离正理想解的距离最小就意味着离负理想解的距离最大。
    weight_new = get_weights(((data - Z.loc['正理想解']) ** 2), topsis_config.WEIGHTS)
    weight_new = np.array(weight_new)

    score_Result = data.copy()
    score_Result['正理想解'] = (np.sqrt(((data - Z.loc['正理想解']) ** 2) * weight_new).sum(axis=1))
    score_Result['负理想解'] = (np.sqrt(((data - Z.loc['负理想解']) ** 2) * weight_new).sum(axis=1))
    # 综合得分指数
    score_Result['综合得分指数'] = score_Result['负理想解'] / (score_Result['负理想解'] + score_Result['正理想解'])

    score_Result = score_Result[['综合得分指数']]
    score_Result.fillna(0)
    score_Result = np.column_stack((pd.DataFrame(uid), pd.DataFrame(score_Result)))
    score_Result = pd.DataFrame(score_Result)
    score_Result.columns = ['uid', 'score']
    score_Result = score_Result.sort_values(by='score', ascending=False)
    return score_Result, new_data


# topsis
def topsis_rank(uid, data, weight=None):
    data = data.fillna(0)
    # 使用排名代替
    d = calculate_rank(data)
    d_orignal = pd.merge(uid, d, how='right', left_index=True, right_on='uid')
    uid = d_orignal['uid_x']
    d_orignal = d_orignal.iloc[:, 3:]
    d_orignal = pd.concat([uid, d_orignal], axis=1).rename(columns={'uid_x': 'uid'})

    d_cal = d.iloc[:, 1:]
    Z = pd.DataFrame([d_cal.min(), d_cal.max()], index=['负理想解', '正理想解'])
    interval = 1 / Z.loc['正理想解']
    new_data = (Z.loc['正理想解'] - d_cal + 1) * interval
    data = new_data

    # 最优最劣方案
    Z = pd.DataFrame([data.min(), data.max()], index=['负理想解', '正理想解'])
    # 求权重使得离正理想解的距离最小。离正理想解的距离最小就意味着离负理想解的距离最大。
    weight_new = get_weights(((data - Z.loc['正理想解']) ** 2), topsis_config.WEIGHTS)
    weight_new = np.array(weight_new)

    score_Result = data.copy()
    score_Result['正理想解'] = (np.sqrt(((data - Z.loc['正理想解']) ** 2) * weight_new).sum(axis=1))
    score_Result['负理想解'] = (np.sqrt(((data - Z.loc['负理想解']) ** 2) * weight_new).sum(axis=1))

    # 综合得分指数
    score_Result['综合得分指数'] = score_Result['负理想解'] / (score_Result['负理想解'] + score_Result['正理想解'])
    score_Result = score_Result[['综合得分指数']]

    score_Result = np.column_stack((pd.DataFrame(uid), pd.DataFrame(score_Result)))
    score_Result = pd.DataFrame(score_Result)
    score_Result.columns = ['uid', 'score']
    score_Result = score_Result.sort_values(by='score', ascending=False)
    return score_Result, d_orignal


def get_groundTruth(fname):
    return readData_zbj.read_Pinfo(fname)['win_id']


# 主执行函数
def do_topsis(fname=None, dataframe=None):
    # print(f'parse  project {fname}')
    fpathname = topsis_config.Data_Dir + fname
    sheet_name = 'bids_sheet'
    grand_truth = get_groundTruth(fname)
    project = readData_zbj.read_Pinfo(fname)
    pid = project['id']
    rate = project['rate']
    uid, dataset = topsis_read_data(fpathname, sheet_name)

    result, rank_data = topsis(uid, dataset)
    maxx = result['score'].max()
    w = 1 / maxx
    # minn = result['score'].min()
    # res = np.sqrt((result['score'] - minn) * w)
    # result['score'] = res
    result['uid'] = result['uid'].astype(int)
    rank_data['uid'] = rank_data['uid'].astype(int)
    result['score'] = result['score'].apply(lambda x: format(x, '.3f')).astype(float)
    z = pd.merge(rank_data, result, on="uid")
    z.loc[z['uid'] == int(grand_truth), 'uid'] = f'{grand_truth}*'
    z = z.sort_values(by='score', ascending=False)
    writer = pd.ExcelWriter(topsis_config.Output_Dir + f'finalscore_{pid}.xls')
    z.to_excel(writer, sheet_name='Sheet1')
    pro_pd = pd.DataFrame(project, index=[0])
    pro_pd.to_excel(writer, sheet_name='Sheet2')
    writer.save()
    axis1 = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
    axis2 = [0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95,
             1]
    # 拉伸分数
    result['score'] = result["score"] * w
    rank_predict = list(result['uid']).index(int(grand_truth)) + 1
    return pid, rank_predict, grand_truth, result, rate


def get_zc_pinfo(pid):
    zcfname = 'pinfo_' + str(pid) + '.xls'
    return


def pinfo_list(timeFrom):
    print("searching project")
    infolist = get_Pinfo_Data_Search(timeFrom='2019')
    print(f'get data num:{len(infolist)}')
    datas = pd.DataFrame(infolist)
    # d_2017 = pd.read_excel('d_fin_2017_ex.xls')
    # d_all = pd.read_excel('p_info_sum.xls')
    # data_use = d_2017[d_2017['date'] > DATA_YEAR]
    data_use = datas['id']
    data_use = ['zbj_' + str(d) + '.xls' for d in data_use]
    return data_use


def get_imlist():
    return [f for f in os.listdir(topsis_config.Data_Dir) if f.endswith('xls')]


def do_scan_topsis(flist, bidnumFilter=1):
    # 获取所有p的列表
    totalnum = len(flist)
    totalnum = 0
    top1rightnum = 0
    top3rightnum = 0
    top5rightnum = 0
    mr = 0
    ratedic = {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
    for fpathname in flist:
        # do_topsis

        try:
            pid, rank_predict, grand_truth, result, rate = do_topsis(fpathname)
            bidnum = len(result)
            if bidnumdic.__contains__(bidnum):
                bidnumdic[bidnum] = bidnumdic[bidnum] + 1
            else:
                bidnumdic[bidnum] = 1
            #  去掉人数为1的重新计算
            if bidnum < bidnumFilter:
                continue
        except ValueError as ve:
            failed_project.append(fpathname)
            print(f"ValueError {ve}")
            print('failed_project :')
            print(failed_project)
            continue
        else:
            ratedic[rate] = ratedic[rate] + 1
            # 这里控制rate分数
            if (rate != 5):
                continue
            totalnum = totalnum + 1
            top1right, top3right, top5right = 0, 0, 0
            if (rank_predict == 1):
                top1right, top3right, top5right = 1, 1, 1
            elif rank_predict <= 3:
                top1right, top3right, top5right = 0, 1, 1
            elif rank_predict <= 5:
                top1right, top3right, top5right = 0, 0, 1
            top1rightnum = top1rightnum + top1right
            top3rightnum = top3rightnum + top3right
            top5rightnum = top5rightnum + top5right
            mr = mr + float(1 / rank_predict)
    print(f'totalnum :{totalnum}, top1rightnum:{top1rightnum},right_rate = {format(top1rightnum / totalnum, ".3f")}')
    print(f'totalnum :{totalnum}, top3rightnum:{top3rightnum},right_rate = {format(top3rightnum / totalnum, ".3f")}')
    print(f'totalnum :{totalnum}, top5rightnum:{top5rightnum},right_rate = {format(top5rightnum / totalnum, ".3f")}')
    print(f'totalnum :{totalnum}, mr:{format(mr, ".2f")},mrr = {format(mr / totalnum, ".3f")}')
    print('ratedic :')
    print(ratedic)


if __name__ == '__main__':
    bidnumdic = {}
    failed_project = []
    # res = get_rank('test.xlsx', sheet_name="Sheet2")
    # res = get_rank('pinfo_231305.xls')
    # print(res)
    # print(topsis_config.Data_Dir)

    # uid, dataset = topsis_read_data(r'C:\Users\zxd\PycharmProjects\spider\data\zbj_pInfo\zbj_21514668.xls', 'bids_sheet')
    #     test one
    # pid, rank_predict, grand_truth, result, rate = do_topsis('zbj_38560911.xls')
    # print(f'pid, rank_predict, grand_truth')
    # print(f'{pid, rank_predict, grand_truth}')
    # print(result, rate)

    #   test all 共545个
    l = pinfo_list(timeFrom='2019')
    do_scan_topsis(l, bidnumFilter=4)
    print('failed_project')
    print(failed_project)

    print('人数情况:')
    print(bidnumdic)
